import type {
    UniversalFlowNode,
    UniversalFlowEdge,
    UniversalFlowNodeData,
    NodeSemanticType
} from '@/models/universalFlow';
import { v4 as uuidv4 } from 'uuid';
import { XMLParser, XMLValidator } from 'fast-xml-parser';
import type { ParseUniversalResult } from '../universalParser';

// Define the structure expected from fast-xml-parser with isArray: true
interface FastXMLParsedObject {
    [key: string]: FastXMLParsedObject[] | FastXMLAttributeObject | string | undefined;
    _attributes?: { [key: string]: string };
    _text?: string;
    _comment?: string; // Assuming commentPropName is used
}

interface FastXMLAttributeObject {
    // Only used to type check _attributes, not actual structure
}

/**
 * Parses XML content using the fast-xml-parser library and transforms
 * the result into the Universal Flow format.
 *
 * @param content The XML string content.
 * @returns A ParseUniversalResult object containing UniversalFlowNodes and Edges.
 */
export function parse(content: string): ParseUniversalResult {
    // 1. Validate FIRST and return immediately if invalid
    const validationResult = XMLValidator.validate(content, {
        allowBooleanAttributes: false,
        // We might need more options here if validator is too lenient
    });

    if (validationResult !== true) { // Check if validation failed
        const errorDetail = validationResult.err || { code: 'Unknown', msg: 'Validation failed', line: 0, col: 0 };
        const errorMsg = `fast-xml-parser Validation Error: ${errorDetail.code} - ${errorDetail.msg} (Line: ${errorDetail.line}, Col: ${errorDetail.col})`;
        console.error('[fastXmlUniversalParser] Validation Failed:', errorMsg); // Log validation failure
        return {
            success: false,
            error: errorMsg,
            nodes: [],
            edges: [],
            rootId: null,
        };
    }
    // --- If validation passed, THEN proceed to parse --- 
    console.log('[fastXmlUniversalParser] Validation Passed. Proceeding to parse...');

    // 2. Parse into JS object
    let parsedObj: FastXMLParsedObject;
    let rootTagName: string | undefined;
    try {
        const parser = new XMLParser({
            ignoreAttributes: false,
            attributeNamePrefix: "", // No prefix
            attributesGroupName: "_attributes", // Group under _attributes
            textNodeName: "_text", // Key for text
            commentPropName: "_comment", // Handle comments
            isArray: (name, jpath, isLeafNode, isAttribute) => !isAttribute && !jpath.endsWith('_text') && !jpath.endsWith('_comment'), // Ensure elements are arrays, but not text/comment
            allowBooleanAttributes: false,
            parseTagValue: false, // Keep values as strings
            parseAttributeValue: false,
            trimValues: true,
            processEntities: true,
            stopNodes: [],
            preserveOrder: false, // Set to true if original XML element order is critical
            ignoreDeclaration: true,
        });

        parsedObj = parser.parse(content);

        // Determine the root tag name (should be the first and only key at the top level)
        const topLevelKeys = Object.keys(parsedObj);
        if (topLevelKeys.length === 0) { // Check for empty object after parsing valid XML (unlikely)
             throw new Error('Parsed object is empty after successful validation.');
        } else if (topLevelKeys.length > 1) {
            // This case should ideally not happen if ignoreDeclaration=true and validation passed
             console.warn('[fastXmlUniversalParser] Warning: Multiple top-level keys found after validation passed and ignoring declaration. Keys:', topLevelKeys);
             // Decide how to handle: maybe pick the first non-declaration key? For now, throw.
             throw new Error('Internal inconsistency: Multiple top-level keys after validation passed.');
        }
        rootTagName = topLevelKeys[0];
        const rootDataArray = parsedObj[rootTagName]; // Assign to a new variable
        // Add more robust check: ensure it's an array and has at least one element
        if (!rootTagName || !Array.isArray(rootDataArray) || rootDataArray.length === 0) {
            throw new Error('Could not identify valid root element data in parsed object.');
        }

    } catch (err: any) {
        console.error('[fastXmlUniversalParser] Parsing Error:', err);
        const errorMsg = err.code && err.line ? `Parser Error: ${err.code} - ${err.msg} (Line: ${err.line}, Col: ${err.col})` : err.message;
        return {
            success: false,
            error: errorMsg || 'An unexpected error occurred during fast-xml-parser processing.',
            nodes: [],
            edges: [],
            rootId: null,
        };
    }

    // 3. Convert JS object structure to Universal Flow elements
    try {
        const rootDataArrayChecked = parsedObj[rootTagName]; // Assign again for type narrowing
        // Now, perform the check again within the try block for TypeScript's flow analysis
        if (!Array.isArray(rootDataArrayChecked) || rootDataArrayChecked.length === 0) {
            // This should theoretically not happen due to the check above, but satisfies TS
             throw new Error('Root element data is not a valid array after parsing.');
        }
        const { nodes, edges, rootId } = transformJsToUniversal(rootDataArrayChecked[0], rootTagName);
        return {
            success: true,
            nodes,
            edges,
            rootId,
            rawDocument: parsedObj // Store the parsed JS object
        };
    } catch (transformError: any) {
        console.error('[fastXmlUniversalParser] Transformation Error:', transformError);
        return {
            success: false,
            error: `Transformation Error: ${transformError.message || 'Unknown error'}`,
            nodes: [],
            edges: [],
            rootId: null,
        };
    }
}

// --- Internal Transformation Logic --- 

interface TransformJsResult {
    nodes: UniversalFlowNode[];
    edges: UniversalFlowEdge[];
    rootId: string | null;
}

/**
 * Transforms the JavaScript object structure from fast-xml-parser
 * into the Universal Flow format.
 *
 * @param rootJsNode The root node object (the first element inside the root tag array).
 * @param rootTagName The tag name of the root node.
 * @returns An object containing the extracted nodes, edges, and the root ID.
 */
function transformJsToUniversal(rootJsNode: FastXMLParsedObject, rootTagName: string): TransformJsResult {
    const nodes: UniversalFlowNode[] = [];
    const edges: UniversalFlowEdge[] = [];
    let rootId: string | null = null;

    /**
     * Recursively processes a JS node object.
     *
     * @param jsNode The current JavaScript node object.
     * @param nodeName The XML tag name for the current node.
     * @param parentUniversalId The ID of the parent UniversalFlowNode (or null if root).
     * @param level The current depth level.
     * @param order The order among siblings being processed.
     * @param parentPath The path of the parent node.
     * @returns The unique ID generated for the newly created UniversalFlowNode.
     */
    function traverse(jsNode: FastXMLParsedObject, nodeName: string, parentUniversalId: string | null, level: number, order: number, parentPath: string): string | null {
        const currentUniversalId = uuidv4();
        const isRoot = !parentUniversalId;

        // --- Path Calculation ---
        const elementSegment = '/' + nodeName;
        // TODO: Add sibling index [n] if needed
        const currentNodePath = parentPath ? parentPath + elementSegment : elementSegment;

        // --- Extract Attributes ---
        const attributes: Record<string, string> = jsNode._attributes || {};

        // --- Determine Semantic Type and Value/Content ---
        let nodeSemanticType: NodeSemanticType = 'element';
        let value: string | number | boolean | null | Date | undefined = undefined;
        let content: string | undefined = undefined;

        if (jsNode._text) {
            // If there's text content, treat it as a value node primarily if no other children exist?
            // Or always create a separate value node? Let's try creating a separate value node for simplicity.
            // If we change this, the traverse logic below needs adjustment.
        }
        if (isRoot) {
            nodeSemanticType = 'root';
        }

        // --- Create Node Data ---
        const nodeData: UniversalFlowNodeData = {
            sourceElement: jsNode, // Keep reference to the original JS object part
            name: nodeName,
            nodeSemanticType: nodeSemanticType,
            attributes: attributes,
            format: 'xml',
            path: currentNodePath,
            level: level,
            order: order,
            childrenIds: [],
            metadata: { xml: {} }, // Initialize xml metadata
        };

        // --- Create Universal Node ---
        const universalNode: UniversalFlowNode = {
            id: currentUniversalId,
            type: 'universal',
            data: nodeData,
            position: { x: 0, y: 0 },
        };
        nodes.push(universalNode);

        if (parentUniversalId) {
            edges.push({
                id: uuidv4(),
                source: parentUniversalId,
                target: currentUniversalId,
                type: 'smoothstep',
                data: { relationshipType: 'parent-child' },
            });
        } else {
            rootId = currentUniversalId;
        }

        // --- Process Children, Text, and Comments ---
        let childOrder = 0;

        // 1. Process Text Node (_text)
        if (typeof jsNode._text === 'string' && jsNode._text.trim()) {
            const textId = uuidv4();
            const textSegment = '/#text[0]'; // Assuming only one text node per element for now
            const textPath = currentNodePath + textSegment;
            const textNodeData: UniversalFlowNodeData = {
                sourceElement: { _text: jsNode._text }, // Simple representation
                name: '',
                value: jsNode._text.trim(),
                attributes: {},
                nodeSemanticType: 'value',
                format: 'xml',
                path: textPath,
                level: level + 1,
                order: childOrder,
            };
            nodes.push({
                id: textId,
                type: 'universal',
                data: textNodeData,
                position: { x: 0, y: 0 },
            });
            edges.push({
                id: uuidv4(),
                source: currentUniversalId,
                target: textId,
                type: 'smoothstep',
                data: { relationshipType: 'parent-child' },
            });
            nodeData.childrenIds?.push(textId);
            childOrder++;
        }

        // 2. Process Comment Nodes (_comment)
        // Assuming _comment might be an array if preserveOrder was true, handle both cases
        const comments = Array.isArray(jsNode._comment) ? jsNode._comment : (jsNode._comment ? [jsNode._comment] : []);
        comments.forEach((commentText, index) => {
            if (typeof commentText === 'string' && commentText.trim()) {
                const commentId = uuidv4();
                const commentSegment = `/#comment[${index}]`;
                const commentPath = currentNodePath + commentSegment;
                const commentNodeData: UniversalFlowNodeData = {
                    sourceElement: { _comment: commentText },
                    name: '',
                    attributes: {},
                    nodeSemanticType: 'comment',
                    format: 'xml',
                    path: commentPath,
                    level: level + 1,
                    order: childOrder,
                    metadata: { comment: commentText.trim() },
                };
                nodes.push({
                    id: commentId,
                    type: 'universal',
                    data: commentNodeData,
                    position: { x: 0, y: 0 },
                });
                edges.push({
                    id: uuidv4(),
                    source: currentUniversalId,
                    target: commentId,
                    type: 'smoothstep',
                    data: { relationshipType: 'parent-child' },
                });
                nodeData.childrenIds?.push(commentId);
                childOrder++;
            }
        });

        // 3. Process Child Elements (Iterate keys, ignoring special ones)
        for (const key in jsNode) {
            if (key === '_attributes' || key === '_text' || key === '_comment') continue;

            const children = jsNode[key];
            if (Array.isArray(children)) {
                children.forEach(childJsNode => {
                    if (typeof childJsNode === 'object' && childJsNode !== null) {
                        const childId = traverse(childJsNode, key, currentUniversalId, level + 1, childOrder, currentNodePath);
                        if (childId) {
                            nodeData.childrenIds?.push(childId);
                            childOrder++;
                        }
                    } else {
                        console.warn(`[fastXmlUniversalParser] Expected object for child element under key '${key}', but got:`, childJsNode);
                    }
                });
            }
        }

        return currentUniversalId;
    }

    // Start traversal from the root JavaScript object node
    traverse(rootJsNode, rootTagName, null, 0, 0, '');

    return { nodes, edges, rootId };
} 